"""
Views which allow users to create and activate accounts.

"""
from datetime import date

from django.conf import settings
from django.core.mail import send_mail
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
from django.template import RequestContext, loader
from django.contrib.auth import logout as auth_logout
from django.contrib.auth.decorators import login_required
from django.contrib.sites.models import Site
from django.utils.hashcompat import sha_constructor     
from django.utils.http import int_to_base36, base36_to_int

from registration.forms import RegistrationForm, EmailChangeForm
from registration.models import RegistrationProfile


def activate(request, activation_key,
             template_name='registration/activate.html',
             extra_context=None):
    """
    Activate a ``User``'s account from an activation key, if their key
    is valid and hasn't expired.
    
    By default, use the template ``registration/activate.html``; to
    change this, pass the name of a template as the keyword argument
    ``template_name``.
    
    **Required arguments**
    
    ``activation_key``
       The activation key to validate and use for activating the
       ``User``.
    
    **Optional arguments**
       
    ``extra_context``
        A dictionary of variables to add to the template context. Any
        callable object in this dictionary will be called to produce
        the end result which appears in the context.
    
    ``template_name``
        A custom template to use.
    
    **Context:**
    
    ``account``
        The ``User`` object corresponding to the account, if the
        activation was successful. ``False`` if the activation was not
        successful.
    
    ``expiration_days``
        The number of days for which activation keys stay valid after
        registration.
    
    Any extra variables supplied in the ``extra_context`` argument
    (see above).
    
    **Template:**
    
    registration/activate.html or ``template_name`` keyword argument.
    
    """
    activation_key = activation_key.lower() # Normalize before trying anything with it.
    account = RegistrationProfile.objects.activate_user(activation_key)
    if extra_context is None:
        extra_context = {}
    context = RequestContext(request)
    for key, value in extra_context.items():
        context[key] = callable(value) and value() or value
    return render_to_response(template_name,
                              { 'account': account,
                                'expiration_days': settings.ACCOUNT_ACTIVATION_DAYS },
                              context_instance=context)


def register(request, success_url=None,
             form_class=RegistrationForm,
             template_name='registration/registration_form.html',
             extra_context=None):
    """
    Allow a new user to register an account.
    
    Following successful registration, issue a redirect; by default,
    this will be whatever URL corresponds to the named URL pattern
    ``registration_complete``, which will be
    ``/accounts/register/complete/`` if using the included URLConf. To
    change this, point that named pattern at another URL, or pass your
    preferred URL as the keyword argument ``success_url``.
    
    By default, ``registration.forms.RegistrationForm`` will be used
    as the registration form; to change this, pass a different form
    class as the ``form_class`` keyword argument. The form class you
    specify must have a method ``save`` which will create and return
    the new ``User``.
    
    By default, use the template
    ``registration/registration_form.html``; to change this, pass the
    name of a template as the keyword argument ``template_name``.
    
    **Required arguments**
    
    None.
    
    **Optional arguments**
    
    ``form_class``
        The form class to use for registration.
    
    ``extra_context``
        A dictionary of variables to add to the template context. Any
        callable object in this dictionary will be called to produce
        the end result which appears in the context.
    
    ``success_url``
        The URL to redirect to on successful registration.
    
    ``template_name``
        A custom template to use.
    
    **Context:**
    
    ``form``
        The registration form.
    
    Any extra variables supplied in the ``extra_context`` argument
    (see above).
    
    **Template:**
    
    registration/registration_form.html or ``template_name`` keyword
    argument.
    
    """
    if request.method == 'POST':
        form = form_class(data=request.POST, files=request.FILES)
        if form.is_valid():
            new_user = form.save()
            # success_url needs to be dynamically generated here; setting a
            # a default value using reverse() will cause circular-import
            # problems with the default URLConf for this application, which
            # imports this file.
            return HttpResponseRedirect(success_url or reverse('registration_complete'))
    else:
        form = form_class()
    
    if extra_context is None:
        extra_context = {}
    context = RequestContext(request)
    for key, value in extra_context.items():
        context[key] = callable(value) and value() or value
    return render_to_response(template_name,
                              { 'form': form },
                              context_instance=context)



                         
def make_hash(user, new_email, timestamp="today"):
    if timestamp == "today":
        timestamp = ((date.today() - date(2009,1,1)).days
                + settings.ACCOUNT_ACTIVATION_DAYS)
    timestamp36 = int_to_base36(timestamp)    
    hash = sha_constructor(settings.SECRET_KEY + unicode(user.id) +
                           user.password + user.email + new_email +
                           unicode(timestamp)).hexdigest()[::2]
    return (timestamp36, hash, new_email)                   

def check_hash(timestamp36, hash, email, user):
    timestamp = base36_to_int(timestamp36)
    timestamp_today = ((date.today() - date(2009,1,1)).days
            + settings.ACCOUNT_ACTIVATION_DAYS)
    if timestamp_today > timestamp:
        return False, "expired confirm link"
    if make_hash(user, email, timestamp) == (timestamp36, hash, email):
        return True, "valid hash"
    return False, "invalid hash"
        


@login_required
def change_email(request):
    if request.method == 'POST':
        form = EmailChangeForm(data=request.POST, user=request.user)
        if form.is_valid():
            values = {"expiration_days":settings.ACCOUNT_ACTIVATION_DAYS,
                      "site":Site.objects.get_current()}
            values["timestamp"], values["hash"], values["email"] = make_hash(
                                       request.user, form.cleaned_data["email"])
            subject = "Email change confirmation for %s" % Site.objects.get_current()
            body = loader.render_to_string("registration/email_change_email.txt",
                                       RequestContext(request, values))
            sender = settings.DEFAULT_FROM_EMAIL
            send_mail(subject, body, sender, [form.cleaned_data["email"]])
            form = None
    else:
        form = EmailChangeForm()
    return render_to_response("registration/email_change.html",
                        { 'form': form,
                          'expiration_days':settings.ACCOUNT_ACTIVATION_DAYS,},
                          context_instance=RequestContext(request)) 
                              
@login_required
def confirm_email(request, timestamp, hash):
    if request.method == "GET" and "email" in request.GET:
        new_email = request.GET["email"]
    
        valid_email, message = check_hash(timestamp, hash, new_email, request.user)
        if valid_email:
            request.user.email = new_email
            request.user.save()
        form = False
    else:
        message = "missing email address"
        valid_email = False
        form = True
    return render_to_response("registration/confirm_email.html",
                              { 'message': message, "form":form,
                                "valid":valid_email},
                              context_instance=RequestContext(request)) 
                              

def logout(request):
    next = request.GET.get("next", "/")
    auth_logout(request)
    return HttpResponseRedirect(next)